package org.jsets.fastboot.oss.service.impl;

import java.io.InputStream;
import java.util.Date;
import java.util.List;
import org.jsets.fastboot.oss.model.dto.FileMetaQuery;
import org.jsets.fastboot.oss.model.entity.FileMeta;
import org.jsets.fastboot.oss.model.event.FileAccessEvent;
import org.jsets.fastboot.oss.service.IFileMetaService;
import org.jsets.fastboot.oss.service.IStorageConfigService;
import org.jsets.fastboot.persistent.support.LambdaQuerier;
import org.jsets.fastboot.persistent.support.LambdaUpdater;
import org.jsets.fastboot.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.eventbus.Subscribe;
import lombok.extern.slf4j.Slf4j;
import org.jsets.fastboot.oss.mapper.FileMetaMapper;

@Slf4j
@Service
public class FileMetaService extends ServiceImpl<FileMetaMapper, FileMeta> implements IFileMetaService {

	@Autowired
	private IStorageConfigService configService;

	@Override
	@Transactional
	public Long insertFile(FileMeta metadata, InputStream inputStream) {
		boolean succeed = this.save(metadata);
		if (succeed) {
			this.configService.getOrCreateEngineService(metadata.getStorageId()).upload(metadata, inputStream);
			return metadata.getId();
		}
		throw new RuntimeException("添加文件失败");
	}
	
	@Override
	public Page<FileMeta> selectPage(FileMetaQuery criteria) {
		boolean byUploadTimeRange = false;
		if (StringUtils.notEmpty(criteria.getUploadTimeStart())&&StringUtils.notEmpty(criteria.getUploadTimeEnd())) {
			byUploadTimeRange = true;
		}
		return LambdaQuerier
				.build(FileMeta.class)
				.setBaseMapper(this.getBaseMapper())
				.setCurrentPage(criteria.getCurrent())
				.setPageSize(criteria.getSize())
				.eqIfNecessary(FileMeta::getBizIdentifier, criteria.getBizIdentifier())
				.eqIfNecessary(FileMeta::getBizType, criteria.getBizType())
				.eqIfNecessary(FileMeta::getExtension, criteria.getExtension())
				.likeIfNecessary(FileMeta::getBucketName, criteria.getBucketName())
				.likeIfNecessary(FileMeta::getAppName, criteria.getAppName())
				.likeIfNecessary(FileMeta::getName, criteria.getName())
				.between(byUploadTimeRange, FileMeta::getUploadTime, criteria.getUploadTimeStart(), criteria.getUploadTimeEnd())
				.orderByDesc(FileMeta::getLastAccessTime)
				.selectPage();
	}

	@Override
	public List<FileMeta> selectList(FileMetaQuery criteria) {
		boolean byUploadTimeRange = false;
		if (StringUtils.notEmpty(criteria.getUploadTimeStart())&&StringUtils.notEmpty(criteria.getUploadTimeEnd())) {
			byUploadTimeRange = true;
		}
		return LambdaQuerier
				.build(FileMeta.class)
				.setBaseMapper(this.getBaseMapper())
				.eqIfNecessary(FileMeta::getBizIdentifier, criteria.getBizIdentifier())
				.eqIfNecessary(FileMeta::getBizType, criteria.getBizType())
				.eqIfNecessary(FileMeta::getExtension, criteria.getExtension())
				.likeIfNecessary(FileMeta::getAppName, criteria.getAppName())
				.likeIfNecessary(FileMeta::getBucketName, criteria.getBucketName())
				.likeIfNecessary(FileMeta::getAppName, criteria.getAppName())
				.likeIfNecessary(FileMeta::getName, criteria.getName())
				.between(byUploadTimeRange, FileMeta::getUploadTime, criteria.getUploadTimeStart(), criteria.getUploadTimeEnd())
				.orderByDesc(FileMeta::getLastAccessTime)
				.selectList();
	}

	@Override
	public long selectCountByBucket(String bucketName) {
		return LambdaQuerier
				.build(FileMeta.class)
				.setBaseMapper(this.getBaseMapper())
				.eq(FileMeta::getBucketName, bucketName)
				.selectCount();
	}

	@Override
	public long selectCountByAppName(String appName) {
		return LambdaQuerier
				.build(FileMeta.class)
				.setBaseMapper(this.getBaseMapper())
				.eq(FileMeta::getAppName, appName)
				.selectCount();
	}

	@Override
	@Transactional
	public void deleteFile(Long id) {
		FileMeta entity = this.getById(id);
		boolean succeed = this.removeById(id);
		if (succeed) {
			this.configService.getOrCreateEngineService(entity.getStorageId()).remove(entity);
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int batchDeleteFile(List<Long> ids) {
		int affected = 0;
		FileMeta entity = null;
		for (Long id : ids) {
			entity = this.getById(id);
			if (null != entity) {
				this.getBaseMapper().deleteById(id);
				this.configService.getOrCreateEngineService(entity.getStorageId()).remove(entity);
				entity = null;
				affected++;
			}
		}
		return affected;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int deleteFileByBiz(String bizId) {
		int affected = 0;
		List<FileMeta> list = LambdaQuerier
					.build(FileMeta.class)
					.setBaseMapper(this.getBaseMapper())
					.eq(FileMeta::getBizIdentifier, bizId)
					.selectList();
		for (FileMeta entity : list) {
			this.getBaseMapper().deleteById(entity.getId());
			this.configService.getOrCreateEngineService(entity.getStorageId()).remove(entity);
			affected++;
		}
		return affected;
	}

	public InputStream getFileInputStream(FileMeta entity) {
		return this.configService.getOrCreateEngineService(entity.getStorageId()).download(entity);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int updateRelations(List<Long> fileIds, String bizIdentifier) {
		int affected = 0;
		LambdaUpdater<FileMeta> updater = new LambdaUpdater<>();
		updater.setBaseMapper(this.getBaseMapper());
		for (Long fileId : fileIds) {
			FileMeta meta = this.getById(fileId);
			if(null!=meta) {
				updater
					.set(FileMeta::getBizIdentifier, bizIdentifier)
					.eq(FileMeta::getId, fileId)
					.update();
				affected++;
			}
		}
		return affected;
	}

	@Override
	@Subscribe
	public void updateAccessInfo(FileAccessEvent evt) {
		this.getBaseMapper().updateAccessInfo(evt.getFileId(), new Date());
		evt = null;
	}

}